{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ddfa9ae6-69fe-444a-b994-8c4c5970a7ec",
   "metadata": {},
   "source": [
    "# Project - Cricket Anaylyst AI Assistant\n",
    "\n",
    "Cricket Analyst AI Assistant is an intelligent tool that analyzes cricket data to compare players, evaluate performances across formats, and provide insightful statistics. It processes historical and recent match data to deliver easy-to-understand summaries, helping fans, analysts, and coaches make informed decisions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8b50bbe2-c0b1-49c3-9a5c-1ba7efa2bcb4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# imports\n",
    "\n",
    "import os\n",
    "import json\n",
    "from dotenv import load_dotenv\n",
    "from openai import OpenAI\n",
    "import gradio as gr\n",
    "import speech_recognition as sr\n",
    "import pandas as pd\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "747e8786-9da8-4342-b6c9-f5f69c2e22ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Initialization\n",
    "\n",
    "load_dotenv(override=True)\n",
    "\n",
    "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
    "if openai_api_key:\n",
    "    print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
    "else:\n",
    "    print(\"OpenAI API Key not set\")\n",
    "    \n",
    "MODEL = \"gpt-4o-mini\"\n",
    "openai = OpenAI()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a521d84-d07c-49ab-a0df-d6451499ed97",
   "metadata": {},
   "outputs": [],
   "source": [
    "system_message = \"\"\"\n",
    "You are a Cricket Analyst AI with deep knowledge of cricket statistics and match analysis.\n",
    "When comparing players, call the `analyze_cricket` tool to get factual data before answering.\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d7be34a6-7288-43b0-ad4e-bbed836cb786",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Sample cricket stats as a list of dicts\n",
    "cricket_data = [\n",
    "    {\"Player\": \"Virat Kohli\", \"Format\": \"ODI\", \"Year\": 2023, \"Runs\": 1377, \"Matches\": 27, \"Average\": 57.37, \"StrikeRate\": 93.21},\n",
    "    {\"Player\": \"Virat Kohli\", \"Format\": \"ODI\", \"Year\": 2022, \"Runs\": 765, \"Matches\": 20, \"Average\": 42.50, \"StrikeRate\": 88.40},\n",
    "    {\"Player\": \"Virat Kohli\", \"Format\": \"ODI\", \"Year\": 2021, \"Runs\": 560, \"Matches\": 15, \"Average\": 40.00, \"StrikeRate\": 90.10},\n",
    "    {\"Player\": \"Babar Azam\", \"Format\": \"ODI\", \"Year\": 2023, \"Runs\": 1454, \"Matches\": 26, \"Average\": 62.00, \"StrikeRate\": 89.50},\n",
    "    {\"Player\": \"Babar Azam\", \"Format\": \"ODI\", \"Year\": 2022, \"Runs\": 1198, \"Matches\": 18, \"Average\": 66.55, \"StrikeRate\": 92.00},\n",
    "    {\"Player\": \"Babar Azam\", \"Format\": \"ODI\", \"Year\": 2021, \"Runs\": 949, \"Matches\": 15, \"Average\": 67.78, \"StrikeRate\": 90.50},\n",
    "    {\"Player\": \"Joe Root\", \"Format\": \"Test\", \"Year\": 2025, \"Runs\": 949, \"Matches\": 15, \"Average\": 69.78, \"StrikeRate\": 95.50},\n",
    "    {\"Player\": \"Joe Root\", \"Format\": \"Test\", \"Year\": 2024, \"Runs\": 2025, \"Matches\": 22, \"Average\": 68.78, \"StrikeRate\": 90.50},\n",
    "    {\"Player\": \"Harry Brook\", \"Format\": \"Test\", \"Year\": 2025, \"Runs\": 1056, \"Matches\": 16, \"Average\": 67.78, \"StrikeRate\": 95.50},\n",
    "    {\"Player\": \"Harry Brook\", \"Format\": \"Test\", \"Year\": 2024, \"Runs\": 2200, \"Matches\": 21, \"Average\": 71.78, \"StrikeRate\": 98.50},\n",
    "\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36bedabf-a0a7-4985-ad8e-07ed6a55a3a4",
   "metadata": {},
   "source": [
    "## Tools\n",
    "\n",
    "Tools starts from here. \n",
    "For this notebook, I have just wrote one Tool, you can add multiple tools for your agent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "46a0a260-b11b-4bde-ab80-911a81e2c281",
   "metadata": {},
   "outputs": [],
   "source": [
    "def analyze_cricket(data_list, player1, match_format=\"ODI\", years=3):\n",
    "    \"\"\"\n",
    "    Return cricket players' performances using an in-memory list of dicts.\n",
    "    \"\"\"\n",
    "    print(\"Tool 'analyze_cricket' is called\")\n",
    "    df = pd.DataFrame(data_list)\n",
    "    latest_year = df['Year'].max()\n",
    "    min_year = latest_year - years + 1\n",
    "\n",
    "    filtered = df[\n",
    "        (df['Format'].str.upper() == match_format.upper()) &\n",
    "        (df['Year'] >= min_year) &\n",
    "        (df['Player'].isin([player1]))\n",
    "    ]\n",
    "    if filtered.empty:\n",
    "        return {\"error\": f\"No data found for {player1} in {match_format} for last {years} years.\"}\n",
    "\n",
    "    summary = filtered.groupby(\"Player\").agg({\n",
    "        \"Matches\": \"sum\",\n",
    "        \"Runs\": \"sum\",\n",
    "        \"Average\": \"mean\",\n",
    "        \"StrikeRate\": \"mean\"\n",
    "    }).round(2)\n",
    "\n",
    "    return summary.reset_index().to_dict(orient=\"records\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cbfd413e-b5d4-42bd-b86f-ed9b4ee360eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example usage:\n",
    "result = analyze_cricket(cricket_data, \"Virat Kohli\", \"ODI\", 3)\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c92699c1-802b-4948-a654-df89e0c19adb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Tool definition \n",
    "analyze_cricket_functions = {\n",
    "        \"name\": \"analyze_cricket\",\n",
    "        \"description\": \"Compare two cricket players' performances over the last N years.\",\n",
    "        \"parameters\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"player1\": {\"type\": \"string\", \"description\": \"Name of first player\"},\n",
    "                # \"player2\": {\"type\": \"string\", \"description\": \"Name of second player\"},\n",
    "                \"match_format\": {\"type\": \"string\", \"enum\": [\"ODI\", \"Test\", \"T20\"], \"description\": \"Format of the match\"},\n",
    "                \"years\": {\"type\": \"integer\", \"description\": \"Number of years to compare\"}\n",
    "            },\n",
    "            \"required\": [\"player1\"]\n",
    "        }\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bdca8679-935f-4e7f-97e6-e71a4d4f228c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# And this is included in a list of tools:\n",
    "\n",
    "tools = [{\"type\": \"function\", \"function\": analyze_cricket_functions}]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3d3554f-b4e3-4ce7-af6f-68faa6dd2340",
   "metadata": {},
   "source": [
    "## Getting OpenAI to use our Tool\n",
    "\n",
    "There's some fiddly stuff to allow OpenAI \"to call our tool\"\n",
    "\n",
    "What we actually do is give the LLM the opportunity to inform us that it wants us to run the tool.\n",
    "\n",
    "Here's how the new chat function looks:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ce9b0744-9c78-408d-b9df-9f6fd9ed78cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "def chat(message, history):\n",
    "    messages = [{\"role\": \"system\", \"content\": system_message}] + history + [{\"role\": \"user\", \"content\": message}]\n",
    "    response = openai.chat.completions.create(model=MODEL, messages=messages, tools=tools)\n",
    "\n",
    "    if response.choices[0].finish_reason==\"tool_calls\":\n",
    "        message = response.choices[0].message\n",
    "        messages.append(message)\n",
    "        for tool_call in message.tool_calls: \n",
    "            response, player1= handle_tool_call(tool_call)\n",
    "            messages.append(response)\n",
    "        response = openai.chat.completions.create(model=MODEL, messages=messages)\n",
    "    return response.choices[0].message.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b0992986-ea09-4912-a076-8e5603ee631f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# We have to write that function handle_tool_call:\n",
    "\n",
    "def handle_tool_call(tool_call):\n",
    "    # tool_call = message.tool_calls[0]\n",
    "    # print(\"tool_call.id\", tool_call.id)\n",
    "    arguments = json.loads(tool_call.function.arguments)\n",
    "    print(\"arguments\", arguments)\n",
    "    player1 = arguments.get('player1')\n",
    "    # player2 = arguments.get('player2')\n",
    "    match_format = arguments.get('match_format', 'ODI')\n",
    "    years = arguments.get('years', 3)\n",
    "    result = analyze_cricket(cricket_data, player1, match_format, years)\n",
    "    print(\"result from analyze_cricket function: \", tool_call.id, result)\n",
    "    response = {\n",
    "        \"role\": \"tool\",\n",
    "        \"content\": json.dumps(result),\n",
    "        \"tool_call_id\": tool_call.id\n",
    "    }\n",
    "    return response, player1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "924e7225-b76d-4518-abad-5bea5c356cf8",
   "metadata": {},
   "source": [
    "# Sample User prompt\n",
    "\n",
    "1. ### Compare Babar and Virat in ODI matches over the last 3 years.\n",
    "Here \n",
    "Player1 is Babar\n",
    "Player2 is Virat\n",
    "match_format is ODI\n",
    "years is 3\n",
    "\n",
    "\n",
    "2. ### can you please give me the comparison of Virat and babar?\n",
    "Here, you are not provided the info on the format and number of years. In this case, the function will pick the default values for the match format, which is ODI, and the years, which is 3.\n",
    "\n",
    "\n",
    "3. ### Compare Rizwan and Babar in ODI Matches over the last years.\n",
    "The given data is not available in the above data list. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4be8a71-b19e-4c2f-80df-f59ff2661f14",
   "metadata": {},
   "outputs": [],
   "source": [
    "gr.ChatInterface(fn=chat, type=\"messages\").launch(inbrowser=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "473e5b39-da8f-4db1-83ae-dbaca2e9531e",
   "metadata": {},
   "source": [
    "# Let's go multi-modal!!\n",
    "\n",
    "We can use DALL-E-3, the image generation model behind GPT-4o, to make us some images\n",
    "\n",
    "Let's put this in a function called artist.\n",
    "\n",
    "### Price alert: each time I generate an image it costs about 4 cents - don't go crazy with images!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2c27c4ba-8ed5-492f-add1-02ce9c81d34c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Some imports for handling images\n",
    "\n",
    "import base64\n",
    "from io import BytesIO\n",
    "from PIL import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "773a9f11-557e-43c9-ad50-56cbec3a0f8f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def artist(player_names):\n",
    "    if len(player_names) <2 or len(player_names) > 2:\n",
    "        return None\n",
    "    player1 = player_names[0]\n",
    "    player2 = player_names[1]\n",
    "    image_response = openai.images.generate(\n",
    "            model=\"dall-e-3\",\n",
    "            prompt=f\"An image representing a comparison of {player1} and {player2}, showing their country flags and bowling or batting style\",\n",
    "            size=\"1024x1024\",\n",
    "            n=1,\n",
    "            response_format=\"b64_json\",\n",
    "        )\n",
    "    image_base64 = image_response.data[0].b64_json\n",
    "    image_data = base64.b64decode(image_base64)\n",
    "    return Image.open(BytesIO(image_data))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d877c453-e7fb-482a-88aa-1a03f976b9e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "image = artist([\"Babar\", \"root\"])\n",
    "display(image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffbfe93b-5e86-4e68-ba71-b301cd5230db",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pydub import AudioSegment\n",
    "from pydub.playback import play\n",
    "\n",
    "def talker(message):\n",
    "    response = openai.audio.speech.create(\n",
    "      model=\"tts-1\",\n",
    "      voice=\"onyx\",    # Also, try replacing onyx with alloy\n",
    "      input=message\n",
    "    )\n",
    "    \n",
    "    audio_stream = BytesIO(response.content)\n",
    "    audio = AudioSegment.from_file(audio_stream, format=\"mp3\")\n",
    "    play(audio)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b88d775d-d357-4292-a1ad-5dc5ed567281",
   "metadata": {},
   "outputs": [],
   "source": [
    "talker(\"Well, hi there\") # For testing purposes"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d48876d-c4fa-46a8-a04f-f9fadf61fb0d",
   "metadata": {},
   "source": [
    "# Our Agent Framework\n",
    "\n",
    "The term 'Agentic AI' and Agentization is an umbrella term that refers to a number of techniques, such as:\n",
    "\n",
    "1. Breaking a complex problem into smaller steps, with multiple LLMs carrying out specialized tasks\n",
    "2. The ability for LLMs to use Tools to give them additional capabilities\n",
    "3. The 'Agent Environment' which allows Agents to collaborate\n",
    "4. An LLM can act as the Planner, dividing bigger tasks into smaller ones for the specialists\n",
    "5. The concept of an Agent having autonomy / agency, beyond just responding to a prompt - such as Memory\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba820c95-02f5-499e-8f3c-8727ee0a6c0c",
   "metadata": {},
   "outputs": [],
   "source": [
    "def chat(history, image_choice):\n",
    "    messages = [{\"role\": \"system\", \"content\": system_message}] + history\n",
    "    response = openai.chat.completions.create(model=MODEL, messages=messages, tools=tools)\n",
    "    image = None\n",
    "    \n",
    "    if response.choices[0].finish_reason==\"tool_calls\":\n",
    "        message = response.choices[0].message\n",
    "        messages.append(message)\n",
    "        player_names = []\n",
    "        for tool_call in message.tool_calls:\n",
    "            response, player1= handle_tool_call(tool_call)\n",
    "            player_names.append(player1)\n",
    "            messages.append(response)\n",
    "        if image_choice.lower() == 'yes':\n",
    "            image = artist(player_names)\n",
    "        else:\n",
    "            print(\"Image value is NO\", image_choice)\n",
    "            \n",
    "        response = openai.chat.completions.create(model=MODEL, messages=messages)\n",
    "        \n",
    "    reply = response.choices[0].message.content\n",
    "    history += [{\"role\":\"assistant\", \"content\":reply}]\n",
    "\n",
    "    # Comment out or delete the next line if you'd rather skip Audio for now..\n",
    "    talker(reply)\n",
    "    \n",
    "    return history, image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f38d0d27-33bf-4992-a2e5-5dbed973cde7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# More involved Gradio code as we're not using the preset Chat interface!\n",
    "# Passing in inbrowser=True in the last line will cause a Gradio window to pop up immediately.\n",
    "\n",
    "with gr.Blocks() as ui:\n",
    "    gr.Markdown(\"### 🏏 Cricket Analyst AI Assistant\")\n",
    "    with gr.Row():\n",
    "        chatbot = gr.Chatbot(height=500, type=\"messages\")\n",
    "        image_output = gr.Image(height=500)\n",
    "\n",
    "    with gr.Row():\n",
    "        image_dropdown = gr.Dropdown(\n",
    "        choices=[\"Yes\", \"No\"],\n",
    "        label=\"Do you want image?\"\n",
    "    )\n",
    "    with gr.Row():\n",
    "        entry = gr.Textbox(label=\"Chat with our AI Assistant:\")\n",
    "    with gr.Row():\n",
    "        clear = gr.Button(\"Clear\")\n",
    "\n",
    "    def do_entry(message, history, image_choice):\n",
    "        history += [{\"role\": \"user\", \"content\": message}]\n",
    "        return \"\", history, image_choice\n",
    "\n",
    "    entry.submit(\n",
    "        do_entry, \n",
    "        inputs=[entry, chatbot, image_dropdown], \n",
    "        outputs=[entry, chatbot, image_dropdown]\n",
    "    ).then(\n",
    "        chat, \n",
    "        inputs=[chatbot, image_dropdown], \n",
    "        outputs=[chatbot, image_output]\n",
    "    )\n",
    "    clear.click(lambda: None, inputs=None, outputs=chatbot, queue=False)\n",
    "\n",
    "ui.launch(inbrowser=True)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
